package sunyu.tools.common;

import org.nutz.lang.Files;
import org.nutz.lang.Streams;
import org.nutz.lang.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 文件工具类
 *
 * @author 孙宇
 */
@Component
public class FileTools {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 将内容写到一个文件末尾，并且添加换行
     * <ul>
     * <li>InputStream - 按二进制方式写入
     * <li>byte[] - 按二进制方式写入
     * <li>Reader - 按 UTF-8 方式写入
     * <li>其他对象被 toString() 后按照 UTF-8 方式写入
     * </ul>
     *
     * @param f   文件
     * @param obj 内容
     */
    public void appendWriteAndRN(File f,
                                 Object obj) {
        Streams.appendWriteAndClose(f, obj.toString() + "\r\n");
    }

    /**
     * 读取 UTF-8 文件全部内容
     *
     * @param path 文件路径
     *
     * @return 文件内容
     */
    public String read(String path) {
        return Files.read(path);
    }

    /**
     * 读取 UTF-8 文件全部内容
     *
     * @param f 文件
     *
     * @return 文件内容
     */
    public String read(File f) {
        return Files.read(f);
    }

    /**
     * 读取文件全部字节，并关闭文件
     *
     * @param path 文件路径
     *
     * @return 文件的字节内容
     */
    public byte[] readBytes(String path) {
        return Files.readBytes(path);
    }

    /**
     * 读取文件全部字节，并关闭文件
     *
     * @param f 文件
     *
     * @return 文件的字节内容
     */
    public byte[] readBytes(File f) {
        return Files.readBytes(f);
    }

    /**
     * 将内容写到一个文件内，内容对象可以是：
     * <ul>
     * <li>InputStream - 按二进制方式写入
     * <li>byte[] - 按二进制方式写入
     * <li>Reader - 按 UTF-8 方式写入
     * <li>其他对象被 toString() 后按照 UTF-8 方式写入
     * </ul>
     *
     * @param path 文件路径，如果不存在，则创建
     * @param obj  内容对象
     */
    public void write(String path,
                      Object obj) {
        Files.write(path, obj);
    }

    /**
     * 将内容写到一个文件内，内容对象可以是：
     * <p>
     * <ul>
     * <li>InputStream - 按二进制方式写入
     * <li>byte[] - 按二进制方式写入
     * <li>Reader - 按 UTF-8 方式写入
     * <li>其他对象被 toString() 后按照 UTF-8 方式写入
     * </ul>
     *
     * @param f   文件
     * @param obj 内容
     */
    public void write(File f,
                      Object obj) {
        Files.write(f, obj);
    }

    /**
     * 将内容写到一个文件末尾
     * <ul>
     * <li>InputStream - 按二进制方式写入
     * <li>byte[] - 按二进制方式写入
     * <li>Reader - 按 UTF-8 方式写入
     * <li>其他对象被 toString() 后按照 UTF-8 方式写入
     * </ul>
     *
     * @param f   文件
     * @param obj 内容
     */
    public void appendWrite(File f,
                            Object obj) {
        Files.appendWrite(f, obj);
    }

    /**
     * 将文件后缀改名，从而生成一个新的文件对象。但是并不在磁盘上创建它
     *
     * @param f      文件
     * @param suffix 新后缀， 比如 ".gif" 或者 ".jpg"
     *
     * @return 新文件对象
     */
    public File renameSuffix(File f,
                             String suffix) {
        return Files.renameSuffix(f, suffix);
    }

    /**
     * 将文件路径后缀改名，从而生成一个新的文件路径。
     *
     * @param path   文件路径
     * @param suffix 新后缀， 比如 ".gif" 或者 ".jpg"
     *
     * @return 新文件后缀
     */
    public String renameSuffix(String path,
                               String suffix) {
        return renameSuffix(path, suffix);
    }

    /**
     * 获取文件主名。 即去掉后缀的名称
     *
     * @param path 文件路径
     *
     * @return 文件主名
     */
    public String getMajorName(String path) {
        return getMajorName(path);
    }

    /**
     * 获取文件主名。 即去掉后缀的名称
     *
     * @param f 文件
     *
     * @return 文件主名
     */
    public String getMajorName(File f) {
        return Files.getMajorName(f);
    }

    /**
     * @see #getSuffixName(String)
     */
    public String getSuffixName(File f) {
        return Files.getSuffixName(f);
    }

    /**
     * 获取文件后缀名，不包括 '.'，如 'abc.gif','，则返回 'gif'
     *
     * @param path 文件路径
     *
     * @return 文件后缀名
     */
    public String getSuffixName(String path) {
        return Files.getSuffixName(path);
    }

    /**
     * @see #getSuffix(String)
     */
    public String getSuffix(File f) {
        return Files.getSuffix(f);
    }

    /**
     * 获取文件后缀名，包括 '.'，如 'abc.gif','，则返回 '.gif'
     *
     * @param path 文件路径
     *
     * @return 文件后缀
     */
    public String getSuffix(String path) {
        return Files.getSuffix(path);
    }

    /**
     * 根据正则式，从压缩文件中获取文件
     *
     * @param zip   压缩文件
     * @param regex 正则式，用来匹配文件名
     *
     * @return 数组
     */
    public ZipEntry[] findEntryInZip(ZipFile zip,
                                     String regex) {
        return Files.findEntryInZip(zip, regex);
    }

    /**
     * 试图生成一个文件对象，如果文件不存在则创建它。 如果给出的 PATH 是相对路径 则会在 CLASSPATH
     * 中寻找，如果未找到，则会在用户主目录中创建这个文件
     *
     * @param path 文件路径，可以以 ~ 开头，也可以是 CLASSPATH 下面的路径
     *
     * @return 文件对象
     *
     * @throws IOException 创建失败
     */
    public File createFileIfNoExists(String path) throws IOException {
        return Files.createFileIfNoExists(path);
    }

    public File createFileIfNoExists2(String path) {
        return Files.createFileIfNoExists2(path);
    }

    /**
     * 如果文件对象不存在，则创建它
     *
     * @param f 文件对象
     *
     * @return 传入的文件对象，以便为调用者省略一行代码
     */
    public File createFileIfNoExists(File f) {
        return Files.createFileIfNoExists(f);
    }

    /**
     * 试图生成一个目录对象，如果文件不存在则创建它。 如果给出的 PATH 是相对路径 则会在 CLASSPATH
     * 中寻找，如果未找到，则会在用户主目录中创建这个目录
     *
     * @param path 文件路径，可以以 ~ 开头，也可以是 CLASSPATH 下面的路径
     *
     * @return 文件对象
     */
    public File createDirIfNoExists(String path) {
        return Files.createDirIfNoExists(path);
    }

    /**
     * 传入一个目录对象，如果目录不存在，则创建目录
     *
     * @param d 文件目录对象
     *
     * @return 文件目录对象，以便调用者省略一行代码
     */
    public File createDirIfNoExists(File d) {
        return Files.createDirIfNoExists(d);
    }

    /**
     * 从 CLASSPATH 下或从指定的本机器路径下寻找一个文件
     *
     * @param path        文件路径
     * @param klassLoader 参考 ClassLoader
     * @param enc         文件路径编码
     *
     * @return 文件对象，如果不存在，则为 null
     */
    public File findFile(String path,
                         ClassLoader klassLoader,
                         String enc) {
        return Files.findFile(path, klassLoader, enc);
    }

    /**
     * 从 CLASSPATH 下或从指定的本机器路径下寻找一个文件
     *
     * @param path 文件路径
     * @param enc  文件路径编码
     *
     * @return 文件对象，如果不存在，则为 null
     */
    public File findFile(String path,
                         String enc) {
        return Files.findFile(path, enc);
    }

    /**
     * 从 CLASSPATH 下或从指定的本机器路径下寻找一个文件
     *
     * @param path        文件路径
     * @param klassLoader 使用该 ClassLoader进行查找
     *
     * @return 文件对象，如果不存在，则为 null
     */
    public File findFile(String path,
                         ClassLoader klassLoader) {
        return Files.findFile(path, klassLoader);
    }


    /**
     * 在一个目录里列出所有的子文件或者目录
     *
     * @param d       目录
     * @param p       正则表达式对象，如果为空，则是全部正则表达式
     * @param exclude true 正则表达式匹配的文件会被忽略，false 正则表达式匹配的文件会被包含
     * @param mode    请参看 LsMode 枚举类说明, null 表示 LsMode.ALL
     *
     * @return 得到文件对象数组
     *
     * @see Files.LsMode
     */
    public File[] ls(File d,
                     final Pattern p,
                     final boolean exclude,
                     Files.LsMode mode) {
        return Files.ls(d, p, exclude, mode);
    }

    /**
     * 列文件
     *
     * @param d     目录对象
     * @param regex 正则表达式
     * @param mode  模式
     *
     * @return 文件列表对象
     *
     * @see #ls(File, Pattern, boolean, Files.LsMode)
     */
    public File[] ls(File d,
                     String regex,
                     Files.LsMode mode) {
        return Files.ls(d, regex, mode);
    }

    /**
     * @see #ls(File, String, Files.LsMode)
     */
    public File[] ls(String path,
                     String regex,
                     Files.LsMode mode) {
        return Files.ls(path, regex, mode);
    }

    /**
     * @see #ls(File, String, Files.LsMode)
     */
    public File[] lsFile(File d,
                         String regex) {
        return Files.lsFile(d, regex);
    }

    /**
     * @see #ls(String, String, Files.LsMode)
     */
    public File[] lsFile(String path,
                         String regex) {
        return Files.lsFile(path, regex);
    }

    /**
     * @see #ls(File, String, Files.LsMode)
     */
    public File[] lsDir(File d,
                        String regex) {
        return Files.lsDir(d, regex);
    }

    /**
     * @see #ls(String, String, Files.LsMode)
     */
    public File[] lsDir(String path,
                        String regex) {
        return Files.lsDir(path, regex);
    }

    /**
     * @see #ls(File, String, Files.LsMode)
     */
    public File[] lsAll(File d,
                        String regex) {
        return Files.lsAll(d, regex);
    }

    /**
     * @see #ls(String, String, Files.LsMode)
     */
    public File[] lsAll(String path,
                        String regex) {
        return Files.lsAll(path, regex);
    }

    /**
     * 从 CLASSPATH 下或从指定的本机器路径下寻找一个文件
     *
     * @param path 文件路径
     *
     * @return 文件对象，如果不存在，则为 null
     */
    public File findFile(String path) {
        return Files.findFile(path);
    }

    /**
     * 从 CLASSPATH 下或从指定的本机器路径下寻找一个文件
     *
     * @param path 文件路径
     *
     * @return 文件对象，如果不存在，则抛出一个运行时异常
     */
    public File checkFile(String path) {
        return Files.checkFile(path);
    }

    /**
     * 获取输出流
     *
     * @param path  文件路径
     * @param klass 参考的类， -- 会用这个类的 ClassLoader
     * @param enc   文件路径编码
     *
     * @return 输出流
     */
    public InputStream findFileAsStream(String path,
                                        Class<?> klass,
                                        String enc) {
        return Files.findFileAsStream(path, klass, enc);
    }

    /**
     * 获取输出流
     *
     * @param path 文件路径
     * @param enc  文件路径编码
     *
     * @return 输出流
     */
    public InputStream findFileAsStream(String path,
                                        String enc) {
        return Files.findFileAsStream(path, enc);
    }

    /**
     * 获取输出流
     *
     * @param path  文件路径
     * @param klass 参考的类， -- 会用这个类的 ClassLoader
     *
     * @return 输出流
     */
    public InputStream findFileAsStream(String path,
                                        Class<?> klass) {
        return Files.findFileAsStream(path, klass);
    }

    /**
     * 获取输出流
     *
     * @param path 文件路径
     *
     * @return 输出流
     */
    public InputStream findFileAsStream(String path) {
        return Files.findFileAsStream(path);
    }

    /**
     * 文件对象是否是目录，可接受 null
     */
    public boolean isDirectory(File f) {
        return Files.isDirectory(f);
    }

    /**
     * 文件对象是否是文件，可接受 null
     */
    public boolean isFile(File f) {
        return Files.isFile(f);
    }

    /**
     * 创建新文件，如果父目录不存在，也一并创建。可接受 null 参数
     *
     * @param f 文件对象
     *
     * @return false，如果文件已存在。 true 创建成功
     *
     * @throws IOException
     */
    public boolean createNewFile(File f) throws IOException {
        return Files.createNewFile(f);
    }

    /**
     * 创建新目录，如果父目录不存在，也一并创建。可接受 null 参数
     *
     * @param dir 目录对象
     *
     * @return false，如果目录已存在。 true 创建成功
     *
     * @throws IOException
     */
    public boolean makeDir(File dir) {
        return Files.makeDir(dir);
    }

    /**
     * 强行删除一个目录，包括这个目录下所有的子目录和文件
     *
     * @param dir 目录
     *
     * @return 是否删除成功
     */
    public boolean deleteDir(File dir) {
        return Files.deleteDir(dir);
    }

    /**
     * 删除一个文件
     *
     * @param f 文件
     *
     * @return 是否删除成功
     *
     * @throws IOException
     */
    public boolean deleteFile(File f) {
        return Files.deleteFile(f);
    }

    /**
     * 清除一个目录里所有的内容
     *
     * @param dir 目录
     *
     * @return 是否清除成功
     */
    public boolean clearDir(File dir) {
        return Files.clearDir(dir);
    }

    /**
     * 相当于 copyFile(src,target,-1)
     *
     * @see #copyFile(File, File, long)
     */
    public boolean copyFile(File src,
                            File target) throws IOException {
        return Files.copyFile(src, target);
    }

    /**
     * 将一个文件 copy 一部分（或者全部）到另外一个文件。如果目标文件不存在，创建它先。
     *
     * @param src    源文件
     * @param target 目标文件
     * @param count  要 copy 的字节数，0 表示什么都不 copy， -1 表示 copy 全部数据
     *
     * @return 是否成功
     *
     * @throws IOException
     */
    public boolean copyFile(File src,
                            File target,
                            long count) throws IOException {
        return Files.copyFile(src, target, count);
    }

    /**
     * @see #copyFile(File, File, long)
     */
    public boolean copyFileWithoutException(File src,
                                            File target,
                                            long count) {
        return Files.copyFileWithoutException(src, target, count);
    }

    /**
     * 自动决定是 copy 文件还是目录
     *
     * @param src    源
     * @param target 目标
     *
     * @return 是否 copy 成功
     */
    public boolean copy(File src,
                        File target) {
        return Files.copy(src, target);
    }

    /**
     * 拷贝一个目录
     *
     * @param src    原始目录
     * @param target 新目录
     *
     * @return 是否拷贝成功
     *
     * @throws IOException
     */
    public boolean copyDir(File src,
                           File target) throws IOException {
        return Files.copyDir(src, target);
    }

    /**
     * 将文件移动到新的位置
     *
     * @param src    原始文件
     * @param target 新文件
     *
     * @return 移动是否成功
     *
     * @throws IOException
     */
    public boolean move(File src,
                        File target) throws IOException {
        return Files.move(src, target);
    }

    /**
     * 将文件改名
     *
     * @param src     文件
     * @param newName 新名称
     *
     * @return 改名是否成功
     */
    public boolean rename(File src,
                          String newName) {
        return Files.rename(src, newName);
    }

    /**
     * 修改路径
     *
     * @param path    路径
     * @param newName 新名称
     *
     * @return 新路径
     */
    public String renamePath(String path,
                             String newName) {
        return Files.renamePath(path, newName);
    }

    /**
     * @param path 路径
     *
     * @return 父路径
     */
    public String getParent(String path) {
        return Files.getParent(path);
    }

    /**
     * @param f 文件对象
     *
     * @return 文件或者目录名
     */
    public String getName(File f) {
        return Files.getName(f);
    }

    /**
     * @param path 全路径
     *
     * @return 文件或者目录名
     */
    public String getName(String path) {
        return Files.getName(path);
    }

    /**
     * 将一个目录下的特殊名称的目录彻底删除，比如 '.svn' 或者 '.cvs'
     *
     * @param dir  目录
     * @param name 要清除的目录名
     *
     * @throws IOException
     */
    public void cleanAllFolderInSubFolderes(File dir,
                                            String name) throws IOException {
        Files.cleanAllFolderInSubFolderes(dir, name);
    }

    /**
     * 精确比较两个文件是否相等
     *
     * @param f1 文件1
     * @param f2 文件2
     *
     * @return <ul>
     * <li>true: 两个文件内容完全相等
     * <li>false: 任何一个文件对象为 null，不存在 或内容不相等
     * </ul>
     */
    public boolean isEquals(File f1,
                            File f2) {
        return Files.isEquals(f1, f2);
    }

    /**
     * 在一个目录下，获取一个文件对象
     *
     * @param dir  目录
     * @param path 文件相对路径
     *
     * @return 文件
     */
    public File getFile(File dir,
                        String path) {
        return Files.getFile(dir, path);
    }

    /**
     * 获取一个目录下所有子目录。子目录如果以 '.' 开头，将被忽略
     *
     * @param dir 目录
     *
     * @return 子目录数组
     */
    public File[] dirs(File dir) {
        return Files.dirs(dir);
    }

    /**
     * 递归查找获取一个目录下所有子目录(及子目录的子目录)。子目录如果以 '.' 开头，将被忽略
     * <p/>
     * <b>包含传入的目录</b>
     *
     * @param dir 目录
     *
     * @return 子目录数组
     */
    public File[] scanDirs(File dir) {
        return Files.scanDirs(dir);
    }


    /**
     * 获取一个目录下所有的文件(不递归，仅仅一层)。隐藏文件会被忽略。
     *
     * @param dir    目录
     * @param suffix 文件后缀名。如果为 null，则获取全部文件
     *
     * @return 文件数组
     */
    public File[] files(File dir,
                        final String suffix) {
        return Files.files(dir, suffix);
    }

    /**
     * 判断两个文件内容是否相等
     *
     * @param f1 文件对象
     * @param f2 文件对象
     *
     * @return <ul>
     * <li>true: 两个文件内容完全相等
     * <li>false: 任何一个文件对象为 null，不存在 或内容不相等
     * </ul>
     */
    public boolean equals(File f1,
                          File f2) {
        return Files.equals(f1, f2);
    }

    public boolean copyOnWrite(File f,
                               Object obj) {
        return Files.copyOnWrite(f, obj);
    }

    public boolean copyOnWrite(String path,
                               Object obj) {
        return Files.copyOnWrite(path, obj);
    }

    public List<String> readLines(File f) {
        return Files.readLines(f);
    }

    public void readLine(File f,
                         Callback<String> callback) {
        Files.readLine(f, callback);
    }

}
